概述
App 端设置SystemUIVisibility的方式几种。一是在任意一个已经显示在窗口上的控件调用View.setSystemUiVisibility(),二是直接在窗口的 LayoutParams.systemUiVisibility 上进行设置并通过WindowManager.updateViewLayout()方法使其生效,还有就是通过 getWindow().getDecorView().getWindowInsetsController().setSystemBarsAppearance(WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS)
的方式。
SystemUI 实现图标变色的方法
代码基于 Android R。
切换应用的时候我们可以看到,不同风格颜色的应用在切换时状态栏的数字和icon图标可能会改变颜色,那么这是怎么实现的呢?
实现这个功能,需要实现DarkReceiver这个接口。
1 | // DarkIconDispatcher.java |
我们就以状态栏的数字时钟为例来介绍。
Clock.java 实现了 DarkReceiver,当收到 onDarkChanged() 回调的时候,会根据 area 和 tint 的值来获取字体需要设置的颜色。
1 | @Override |
然后在 onAttachedToWindow() 中注册一下监听,那么当应用的变化时就会通过 DarkIconDispatcherImpl 来发送通知。
1 | @Override |
前面我们介绍了数字时钟 Clock 的变色原理,现在再来介绍一下 StatusBarIconView 是如何变色的,其实原理是一样的。
NotificationIconAreaController 也实现了 DarkReceiver 接口,当 SystemUIAppearance 变化时也会通知到它。
1 | DarkIconDispatcherImpl.applyIconTint() |
那么什么时候会回调呢?下面我们就来介绍一下这个流程。
App
首先要有 App 端设置当前应用的 StatusBar 类型,比如:View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 或者 WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS。
先看通过 View.setSystemUiVisibility() 来设置:
1 | // View.java |
然后在addWindow时会调用 ViewRootImpl.collectViewAttributes() 方法收集控件树中每个 View 所保存的 SystemUIVisibility。 如下:
1 | ActivityThread.handleResumeActivity() |
1 | private boolean collectViewAttributes() { |
1 | View.java |
计算 insetsFlags.appearance
1 | ViewRootImpl.java |
1 | ViewRootImpl.doTraversal() |
将 WindowManager.LayoutParams 传递到 server 端。会把LayoutParams.subtreeSystemUiVisibility、insetsFlags.appearance 以及LayoutParams.sytemUiVisibility字段送入WMS.
system_server
1 | WindowManagerService.relayoutWindow() |
1 | WindowManagerService.java |
然后获取当前的top window的SystemUiVisibility得到SystemUiVisibility数组,然后传给SystemUI。
1 | DisplayPolicy.updateSystemUiVisibilityLw() |
1 | int updateSystemUiVisibilityLw() { |
SystemUI
SystemUI根据system_server传递过来的 AppearanceRegion[] 的appearance得到当前需要的状态栏图标的颜色。
1 | CommandQueue.onSystemBarAppearanceChanged() |
如果状态有变化,就更新状态:
1 | // LightBarController.java |
AppearanceRegion 是指定区域指定 mAppearance,后面SystemUI会根据这个 mAppearance来计算出需要显示的颜色值。
1 | private void animateIconTint(float targetDarkIntensity, long delay, |
1 | private void setIconTintInternal(float darkIntensity) { |
1 | LightBarTransitionsController.setIconTintInternal() |
根据 mDarkIntensity 来得到 mIconTint。
1 | @Override |
根据 mIconTint 来得到图标颜色值。
1 | static int getTint(Rect tintArea, View view, int color) { |
决定状态栏颜色的属性依次是:
LightBarController.mAppearanceRegions[] -> LightBarTransitionsController.mDarkIntensity -> DarkIconDispatcher.mIconTint
参考文章
https://blog.csdn.net/csdnxialei/article/details/95059041
https://www.codeleading.com/article/3648999178/